package home

import (
	"adai.design/homeserver/home/model"
	"encoding/json"
	"time"
	"fmt"
	"adai.design/homeserver/log"
	"adai.design/homeserver/home/data"
	"adai.design/homeserver/members"
)

// 自动化触发条件
type event struct {
	Type 	string				`json:"type" bson:"type"`
	Desc 	json.RawMessage		`json:"desc" bson:"desc"`
	data 	interface{}
}

type eventTimer struct {
	MinuteOfDay 	int		`json:"timer" bson:"timer"`
	Week 			uint8	`json:"week" bson:"week"`
}

func (t *eventTimer) check(now time.Time) bool {
	minuteOfDay := now.Minute() + now.Hour() * 60
	if minuteOfDay != t.MinuteOfDay {
		return false
	}
	// 检查星期
	if t.Week & (0x01 << uint8(now.Weekday())) == 0x00 {
		return false
	}
	return true
}

// 定时器触发自动化推送标题信息
// 如: 早上7:10  下午: 3:20
func (t *eventTimer) title() string {
	title := ""
	if t.MinuteOfDay / 60 < 12 {
		title += "上午"
	} else {
		title += "下午"
	}

	return fmt.Sprintf("%s %d:%02d", title, t.MinuteOfDay/60, t.MinuteOfDay%60)
}

func newEventTimer(data []byte) *eventTimer {
	var event eventTimer
	err := json.Unmarshal(data, &event)
	if err != nil {
		log.Error("%s", err)
		return nil
	}
	return &event
}

const (
	thresholdTypeEqual = 0
	thresholdTypeGreater = 1
	thresholdTypeSmaller = 2
)

type eventCharacteristic struct {
	Aid 			string		`json:"aid" bson:"aid"`
	SId 			int			`json:"sid" bson:"sid"`
	CId 			int			`json:"cid" bson:"cid"`
	Value 			interface{}	`json:"value" bson:"value"`
	ThresholdType	int 		`json:"threshold_t" bson:"threshold_t"`
	last 			time.Time
}

func toIntValue(value interface{}) (int, error) {
	switch t := value.(type) {
	case int:
		return t, nil
	case int8:
		return int(t), nil
	case uint8:
		return int(t), nil
	case int32:
		return int(t), nil
	case float32:
		return int(t), nil
	case float64:
		return int(t), nil
	case bool:
		if t == true {
			return 1, nil
		} else {
			return 0, nil
		}
	}
	return 0, fmt.Errorf("unsupport threshold type value=%v", value)
}

func toFloatValue(value interface{}) (float32, error) {
	switch t := value.(type) {
	case int:
		return float32(t), nil
	case int8:
		return float32(t), nil
	case uint8:
		return float32(t), nil
	case int32:
		return float32(t), nil
	case float32:
		return t, nil
	case float64:
		return float32(t), nil
	case bool:
		if t == true {
			return 1, nil
		} else {
			return 0, nil
		}
	}
	return 0, fmt.Errorf("unsupport threshold type value=%v", value)
}

func (c *eventCharacteristic) check(h *Home, char *data.Characteristic) bool {
	if c.Aid != char.AId ||
		c.SId != char.SId ||
		c.CId != char.CId {
		return false
	}

	cchar := h.containerManager.getCharacteristic(c.Aid, c.SId, c.CId)
	if cchar == nil {
		return false
	}

	result := func() bool {
		switch cchar.Format {
		case model.FormatBool, model.FormatUInt8, model.FormatUInt16, model.FormatUInt32, model.FormatInt32:
			current, err := toIntValue(char.Value)
			if err != nil {
				log.Error("event characteristic check err: %s", err)
				return false
			}

			target, err := toIntValue(c.Value)
			if err != nil {
				log.Error("event characteristic check err: %s", err)
				return false
			}

			if c.ThresholdType == thresholdTypeEqual {
				if current == target {
					return true
				}
			} else if c.ThresholdType == thresholdTypeGreater {
				if current >= target {
					return true
				}
			} else if c.ThresholdType == thresholdTypeSmaller {
				if current <= target {
					return true
				}
			}
			return false

		case model.FormatFloat:
			current, err := toFloatValue(char.Value)
			if err != nil {
				log.Error("event characteristic check err: %s", err)
				return false
			}

			target, err := toFloatValue(c.Value)
			if err != nil {
				log.Error("event characteristic check err: %s", err)
				return false
			}

			if c.ThresholdType == thresholdTypeEqual {
				if current == target {
					return true
				}
			} else if c.ThresholdType == thresholdTypeGreater {
				if current >= target {
					return true
				}
			} else if c.ThresholdType == thresholdTypeSmaller {
				if current <= target {
					return true
				}
			}
			return false

		default:
			return false
		}
	}()

	if result == false {
		return false
	} else if cchar.Type == model.TypeCurrentRelativeHumidity || cchar.Type == model.TypeCurrentTemperature {
	// 温湿度触发自动化执行临时解决方案
	// 当达到开启温度条件时，每分钟检查一次是否需要执行自动化
	// 需要做的最终效果(当自动化执行的动作全部已经达到指定状态，不在重复执行)
		now := time.Now()
		if c.last.Add(time.Minute).After(now) == false {
			c.last = now
			log.Debug("mark")
			return true
		}
		return false
	}
	return result
}

// 属性触发自动化推送标题内容
// 如: 卧室温湿度计的温度大于38°
// 如: 卧室温湿度计的湿度大于80%
// 如: 卧室的大门打开了
// 如: 卧室的大灯打开了
func (c *eventCharacteristic) title(h *Home) string {
	cchar := h.containerManager.getCharacteristic(c.Aid, c.SId, c.CId)
	if cchar == nil {
		return ""
	}

	service := h.serviceManager.getService(&data.Characteristic{
		AId: c.Aid, SId: c.SId,
	})
	if service == nil {
		return ""
	}

	title := h.getRoomNameById(service.Room)

	// 暂时只处理这两种
	switch cchar.Type {
	case model.TypeCurrentTemperature:
		if c.ThresholdType == thresholdTypeGreater {
			title += fmt.Sprintf("的温度大于%v°", c.Value)
			return title
		} else if c.ThresholdType == thresholdTypeSmaller {
			title += fmt.Sprintf("的温度小于%v°", c.Value)
			return title
		}

	case model.TypeCurrentRelativeHumidity:
		if c.ThresholdType == thresholdTypeGreater {
			title += fmt.Sprintf("的湿度大于%v%%", c.Value)
			return title
		} else if c.ThresholdType == thresholdTypeSmaller {
			title += fmt.Sprintf("的湿度小于%v%%", c.Value)
			return title
		}
	}
	return ""
}

func newEventCharacteristic(data []byte) *eventCharacteristic {
	var event eventCharacteristic
	err := json.Unmarshal(data, &event)
	if err != nil {
		log.Error("%s", err)
		return nil
	}
	return &event
}

// 位置自动化触发条件，
// 有人离开，有人到达，所有人离开，所有人达到
const (
	// 有人达到或者离开
	locationEventLeave 		= 	0
	locationEventArrive 	= 	1
	// 第一个人达到
	locationEventFirstArrive = 	3
	// 最后一个人离开
	locationEventLastLeave	= 	4
)

type eventLocation struct {
	Members 	[]string		`json:"members" bson:"members"`
	TriggerType int 			`json:"trigger_t" bson:"trigger_t"`
}

func NewEventLocation(data []byte) *eventLocation {
	var event eventLocation
	err := json.Unmarshal(data, &event)
	if err != nil {
		log.Error("%s", err)
		return nil
	}
	return &event
}

// eg: 光头云 达到 [云の家]
// eg: 光头云 离开了 [云の家]
func (l *eventLocation) title(h *Home, m *Member) string {
	name := ""
	if membership := members.GetMembership(m.Id); membership != nil {
		name = membership.Name
	}
	state := ""
	switch l.TriggerType {
	case locationEventArrive:
		state = "达到"
	case locationEventLeave:
		state = "离开了"
	case locationEventFirstArrive:
		state = "达到"
	case locationEventLastLeave:
		state = "离开了"
	}
	return name + "" + state + " [" + h.Name + "]"
}


func (l *eventLocation) check(h *Home, m *Member) bool {
	// 检查是否在自动化触发的成员列表里面
	in := false
	for _, member := range l.Members {
		if member == m.Id {
			in = true
		}
	}
	if in == false {
		return false
	}

	// 判断条件
	switch l.TriggerType {
	case locationEventLeave:
		if m.State == MemberStateLeave {
			return true
		}
	case locationEventArrive:
		if m.State == MemberStateArrive {
			return true
		}

		// 第一个达到
	case locationEventFirstArrive:
		if m.State == MemberStateArrive {
			for _, id := range l.Members {
				if id != m.Id {
					member := h.memberManager.getMember(id)
					if  member == nil || member.State == MemberStateArrive {
						return false
					}
				}
			}
			return true
		}

		// 最后一个离开
	case locationEventLastLeave:
		if m.State == MemberStateLeave {
			for _, id := range l.Members {
				if id != m.Id {
					member := h.memberManager.getMember(id)
					if member != nil && member.State == MemberStateArrive {
						return false
					}
				}
			}
			return true
		}
	}
	return false
}


//  自动化执行条件
type condition struct {
	Type 	string				`json:"type" bson:"type"`
	Desc 	json.RawMessage		`json:"desc" bson:"desc"`
	data 	interface{}
}

type conditionPeriod struct {
	After 		int 		`json:"after" bson:"after"`
	Before 		int 		`json:"before" bson:"before"`
}

func (p *conditionPeriod) check(now time.Time) bool {
	minuteOfDay := now.Minute() + now.Hour() * 60
	// 不跨天的时间段
	if p.Before < p.After {
		if minuteOfDay >= p.Before && minuteOfDay <= p.After {
			return true
		}
		return false
	} else {
		// 跨天时间段
		// 早上或者夜间时间段内
		if minuteOfDay < p.Before || minuteOfDay > p.After {
			return true
		}
		return false
	}
	return false
}


func newConditionPeriod(data []byte) *conditionPeriod {
	var condition conditionPeriod
	err := json.Unmarshal(data, &condition)
	if err != nil {
		log.Error("%s", err)
		return nil
	}
	return &condition
}


const (
	// 所有人离开/达到
	locationConditionAllLeave 		= 	0
	locationConditionAllArrive 		= 	1
	// 有人在家
	locationConditionSomeoneArrive = 	2
)

type conditionLocation struct {
	Members 	[]string		`json:"members" bson:"members"`
	ConditionType 	int 		`json:"trigger_t" bson:"trigger_t"`
}

func (l *conditionLocation) check(h *Home) bool {
	switch l.ConditionType {
	// 所有人达到
	case locationConditionAllArrive:
		for _, id := range l.Members {
			member := h.memberManager.getMember(id)
			if member == nil || member.State == MemberStateLeave {
				return false
			}
		}
		return true

		// 所有人都离开
	case locationConditionAllLeave:
		for _, id := range l.Members {
			member := h.memberManager.getMember(id)
			if member == nil || member.State == MemberStateArrive {
				return false
			}
		}
		return true

		// 有人在家
	case locationConditionSomeoneArrive:
		for _, id := range l.Members {
			member := h.memberManager.getMember(id)
			if member != nil && member.State == MemberStateArrive {
				return true
			}
		}
		return false
	}
	return false
}


func newConditionLocation(data []byte) *conditionLocation {
	var condition conditionLocation
	err := json.Unmarshal(data, &condition)
	if err != nil {
		log.Error("%s", err)
		return nil
	}
	return &condition
}

type conditionCharacteristic struct {
	Aid 			string		`json:"aid" bson:"aid"`
	SId 			int			`json:"sid" bson:"sid"`
	CId 			int			`json:"cid" bson:"cid"`
	Value 			interface{}	`json:"value" bson:"value"`
	ThresholdType	int 		`json:"threshold_t" bson:"threshold_t"`
}

func (c *conditionCharacteristic) check(h *Home) bool {
	cchar := h.containerManager.getCharacteristic(c.Aid, c.SId, c.CId)
	if cchar == nil {
		return false
	}

	switch cchar.Format {
	case model.FormatBool, model.FormatUInt8, model.FormatUInt16, model.FormatUInt32, model.FormatInt32:
		current, err := toIntValue(cchar.Value)
		if err != nil {
			log.Error("event characteristic check err: %s", err)
			return false
		}

		target, err := toIntValue(c.Value)
		if err != nil {
			log.Error("event characteristic check err: %s", err)
			return false
		}

		if c.ThresholdType == thresholdTypeEqual {
			if current == target {
				return true
			}
		} else if c.ThresholdType == thresholdTypeGreater {
			if current >= target {
				return true
			}
		} else if c.ThresholdType == thresholdTypeSmaller {
			if current <= target {
				return true
			}
		}
		return false

	case model.FormatFloat:
		current, err := toFloatValue(cchar.Value)
		if err != nil {
			log.Error("event characteristic check err: %s", err)
			return false
		}

		target, err := toFloatValue(c.Value)
		if err != nil {
			log.Error("event characteristic check err: %s", err)
			return false
		}

		if c.ThresholdType == thresholdTypeEqual {
			if current == target {
				return true
			}
		} else if c.ThresholdType == thresholdTypeGreater {
			if current >= target {
				return true
			}
		} else if c.ThresholdType == thresholdTypeSmaller {
			if current <= target {
				return true
			}
		}
		return false

	default:
		return false
	}
	return false
}

func newConditionCharacteristic(data []byte) *conditionCharacteristic {
	var condition conditionCharacteristic
	if err := json.Unmarshal(data, &condition); err != nil {
		log.Error("%s", err)
		return nil
	}
	return &condition
}
